﻿using System.Collections.Generic;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Services;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace Noris.Schedule.UI.Services
{
	#region GridPainterCls : Správce objektů typu IElementPainter
	/// <summary>
	/// GridPainterCls : Správce objektů typu IElementPainter
	/// </summary>
	public class GridPainterCls : IDisposable
	{
		#region KONSTRUKCE
		public GridPainterCls()
		{
			this._Locker = new object();
			this._GridPainterDict = new Dictionary<GraphElementPainterKey, IGraphElementPainter>();
		}
		private object _Locker;
		private Dictionary<GraphElementPainterKey, IGraphElementPainter> _GridPainterDict;
		public void Dispose()
		{
			this._GridPainterDict.Clear();
		}
		#endregion
		#region VYHLEDÁNÍ PAINTERU PODLE JEHO KLÍČE, NALEZENÍ PAINTERU PODLE JEHO VHODNOSTI
		/// <summary>
		/// Pro daný klíč najde a vrátí painter.
		/// Klíčem je typ grafu, třída dat a gafický typ elementu.
		/// Painter je služba typu IGraphElementPainter.
		/// Konkrétní painter je vybrán ze seznamu existujících poskytovatelů této služby (může jich být více, dokonce i uživatelsky přidávané).
		/// Princip výběru je snadný: každého painteru se zeptám, jak rád by kreslil daný element (specifikován je předaným klíčem).
		/// Každý painter vrátí hodnotu: 0 (a záporné) = nechce kreslit, kladné číslo = vyjadřuje touhu kreslit, čím vyšší číslo tím vyšší touha.
		/// Painter s nejvyšší touhou ze všech je tedy zvolen pro kreslení.
		/// Při příštím požadavku na kreslení stejného klíče už dotazování neprobíhá, je rovnou vrácen painter dříve vybraný.
		/// </summary>
		/// <param name="graphMode">Typ grafu</param>
		/// <param name="classNumber">Číslo třídy</param>
		/// <param name="drawType">Typ elementu</param>
		/// <returns></returns>
		public IGraphElementPainter GetPainter(RowGraphMode graphMode, int classNumber, GraphElementLayerType layerType, GraphElementShapeType elementShapeType, object graphTag)
		{
			// 1. Pokud pro daný klíč už mám vybraného favorita, rovnou jej vrátím:
			GraphElementPainterKey elementKey = new GraphElementPainterKey(graphMode, classNumber, layerType, elementShapeType, graphTag);
			return this.GetPainter(elementKey);
		}
		/// <summary>
		/// Pro daný klíč najde a vrátí painter.
		/// Klíčem je typ grafu, třída dat a gafický typ elementu.
		/// Painter je služba typu IGraphElementPainter.
		/// Konkrétní painter je vybrán ze seznamu existujících poskytovatelů této služby (může jich být více, dokonce i uživatelsky přidávané).
		/// Princip výběru je snadný: každého painteru se zeptám, jak rád by kreslil daný element (specifikován je předaným klíčem).
		/// Každý painter vrátí hodnotu: 0 (a záporné) = nechce kreslit, kladné číslo = vyjadřuje touhu kreslit, čím vyšší číslo tím vyšší touha.
		/// Painter s nejvyšší touhou ze všech je tedy zvolen pro kreslení.
		/// Při příštím požadavku na kreslení stejného klíče už dotazování neprobíhá, je rovnou vrácen painter dříve vybraný.
		/// </summary>
		/// <param name="elementKey">Klíč elementu</param>
		/// <returns></returns>
		public IGraphElementPainter GetPainter(GraphElementPainterKey elementKey)
		{
			IGraphElementPainter value;
			if (this._GridPainterDict.TryGetValue(elementKey, out value))
				return value;

			// 2. Pro daný klíč nevím, který painter bude ten pravý - zeptám se jich, zda - a jak moc by chtěli kreslit:
			lock (this._Locker)
			{
				if (this._GridPainterDict.TryGetValue(elementKey, out value)) return value;

				this._FindPainter(elementKey);

				if (this._GridPainterDict.TryGetValue(elementKey, out value)) return value;
			}
			return null;
		}
		/// <summary>
		/// Ze všech dostupných painterů vybere ten, který se pro daný klíč hodí nejvíc (to je ten, který nejvíc chce kreslit)
		/// </summary>
		/// <param name="elementKey">Klíč elementu</param>
		private void _FindPainter(GraphElementPainterKey elementKey)
		{
			IEnumerable<IGraphElementPainter> painters = Steward.GetServiceObjectList<IGraphElementPainter>(true);
			IGraphElementPainter bestPainter = null;
			float bestRatio = 0F;
			foreach (IGraphElementPainter painter in painters)
			{
				if (painter == null) continue;

				// Zeptáme se postupně každého poskytovatele služby IGraphElementPainter, jak rád by kreslil náš element:
				float thisRatio = painter.GetPriority(elementKey);

				if (thisRatio <= 0F) continue;                // Nechce kreslit?   No nic, nutit ho nebudeme.
				if (bestRatio == 0F || thisRatio > bestRatio) // Chce kreslit, a víc než všechny dřívější?
				{
					bestPainter = painter;
					bestRatio = thisRatio;
				}
			}
			this._GridPainterDict.Add(elementKey, bestPainter);      // Do vnitřní paměti si uložím referenci na objekt poskytovatele, který chce kreslit.
		}
		#endregion
	}
	#endregion
}
